package iot.core.manager.htcll;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import iot.core.annotation.Auth;
import iot.core.annotation.Log;
import iot.core.data.PageData;
import iot.core.data.entity.HTAction;
import iot.core.data.entity.HTCls;
import iot.core.data.entity.HTCodeLog;
import iot.core.data.entity.HTCtl;
import iot.core.data.mapper.*;
import iot.core.enums.AuthType;
import iot.core.manager.htcls.HTClsColDto;
import iot.core.manager.htcls.HTClsDto;
import iot.core.manager.htcls.HTClsManager;
import iot.core.manager.id.HTIDManager;
import iot.core.springboot.ApplicationUtils;
import iot.core.springboot.properties.ProjectInfoProperty;
import iot.core.util.MyJSON;
import iot.core.util.StreamUtil;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class HTCtlManager {

    ProjectInfoProperty projectInfoProperty;
    HTCodeLogMapper htCodeLogMapper;

    HTClsMapper htClsMapper;
    HTCtlMapper htCtlMapper;
    HTActionMapper htActionMapper;


    private static volatile HTCtlManager instance = null;


    //2.构造方法设为 private
    public HTCtlManager() {
        projectInfoProperty= ApplicationUtils.getBean(ProjectInfoProperty.class);
        htCodeLogMapper= ApplicationUtils.getBean(HTCodeLogMapper.class);
        htCtlMapper= ApplicationUtils.getBean(HTCtlMapper.class);
        htActionMapper= ApplicationUtils.getBean(HTActionMapper.class);
        htClsMapper= ApplicationUtils.getBean(HTClsMapper.class);


    }

    public static HTCtlManager getInstance() {
        if (instance == null) {
            synchronized (HTCtlManager.class) {
                if (instance == null) {
                    instance = new HTCtlManager();
                }
            }
        }
        return instance;
    }


    List<HTCtlDto> lCtlAll;
    List<HTCtlActDto> lActAll;

    public void start() {
        lCtlAll=new ArrayList<>();
        lActAll=new ArrayList<>();
        Reflections reflections = new Reflections("iot");
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(Tag.class);
        for (Class<?> clazz : annotated) {
            var mcls = getHTClsDto(clazz);
            if(mcls!=null){
                lCtlAll.add(mcls);
            }
        }
        var l1= lCtlAll.stream().map(a->a.getActions()  )
                .flatMap(a->a.stream())
                .collect(Collectors.toList());
        lActAll=l1;

    }

    HTCtlDto getHTClsDto(Class<?> t){
        var RequestMapping= t.getAnnotation(RequestMapping.class);
        if(RequestMapping==null){
            return null;
        }

        var mcls=new HTCtlDto();
        mcls.setEName(t.getSimpleName() );
        mcls.setName(t.getSimpleName());
        mcls.setClazz(t);
        mcls.setMemo("");
        mcls.setRoute(RequestMapping.value()[0])  ;
        mcls.setFullName(t.getName());
        var apiModel= t.getAnnotation(Tag.class);
        if(apiModel!=null){
            mcls.setName(apiModel.name() );
            mcls.setMemo(apiModel.description() );
        }
        getHTCtlActDto(mcls);
        return mcls;
    }
    void getHTCtlActDto (HTCtlDto mcls){
        Class<?> t = mcls.getClazz();

        var r = new ArrayList<HTCtlActDto>();
        mcls.setActions(r);

        var methods = t.getDeclaredMethods( );
        for(var p:methods){
            var apiModel= p.getAnnotation(Operation.class);
            if(apiModel==null){
                continue;
            }


            var url="";
            if("".equals(url)){
                var mapping= p.getAnnotation(PostMapping.class);
                if(mapping!=null){
                    url=mapping.value()[0];
                }
            }
            if("".equals(url)){
                var mapping= p.getAnnotation(GetMapping.class);
                if(mapping!=null){
                    url=mapping.value()[0];
                }
            }
            if("".equals(url)){
                var mapping= p.getAnnotation(RequestMapping.class);
                if(mapping!=null){
                    url=mapping.value()[0];
                }
            }

            if("".equals(url)) {
                    continue;
            }

            var f=new HTCtlActDto();
            f.setEName(p.getName());
            f.setName(apiModel.summary() );
            f.setMemo(apiModel.description());
            f.setAuthType(AuthType.None);
            f.setFullUrl(mcls.getRoute()+url);
            f.setLogTemplete("");
            f.setMinfo(p);
            f.setCtlFullName(mcls.getFullName());

            var auth= p.getAnnotation(Auth.class);
            if(auth!=null){
                f.setAuthType(auth.authType());
            }

            var log=p.getAnnotation(Log.class);
            if(log!=null){
                f.setLogNeed(log.logNeed());
                if(StringUtils.hasText(log.logTemplete())){
                    f.setLogTemplete(log.logTemplete());
                }
                f.setLogPin(log.logPin());
                f.setLogPout(log.logPout());
            }

            if("".equals(f.getLogTemplete())){
                if(f.getEName().startsWith("delete")){
                    f.setLogTemplete("{ActionName} {Deleted.Name}");
                }else{
                    f.setLogTemplete("{ActionName} {Pin.Name}");
                }
            }

            var ps = p.getParameters();
            for ( var mps : ps )
            {
                var req=mps.getAnnotation(RequestParam.class);
                if(req!=null){
                    setPinType(f,mps);
                }

                var requestBody=mps.getAnnotation(RequestBody.class);
                if(requestBody!=null){
                    setPinType(f,mps);
                }
            }
            f.setPoutType(p.getReturnType().getName() );
            if(List.class.isAssignableFrom(p.getReturnType() ) )
            {
                f.setPoutIsList(true);
                ParameterizedType py=(ParameterizedType)(p.getGenericReturnType());
                f.setPoutType(  py.getActualTypeArguments()[0].getTypeName() );
            }
            if(PageData.class.isAssignableFrom(p.getReturnType()  ) ) {
                f.setPoutIsPage(true);
                ParameterizedType py=(ParameterizedType)(p.getGenericReturnType());
                f.setPoutType(  py.getActualTypeArguments()[0].getTypeName() );
            }
            r.add(f);
        }

    }
    void setPinType(HTCtlActDto f, Parameter mps){
        f.setPinName(mps.getName());
        f.setPinType(mps.getType().getTypeName() );
        if(List.class.isAssignableFrom(mps.getType() ) )
        {
            f.setPinIsList(true);
            ParameterizedType py=(ParameterizedType)(mps.getParameterizedType());
            f.setPinType(  py.getActualTypeArguments()[0].getTypeName() );

        }

    }
    HTCodeLog createHTCodeLogByDto(HTCtl mdb){
        var mcodelog=new HTCodeLog();
        mcodelog.setId(HTIDManager.getInstance().nextLong());
        mcodelog.setBId(mdb.getId());
        mcodelog.setCrt_DT(new Date());
        mcodelog.setFullName(mdb.getCtlFullName());
        mcodelog.setLX("Ctl");
        mcodelog.setMemo("");
        mcodelog.setName(mdb.getName());
        mcodelog.setPrj_Name(mdb.getPrj_Name());
        return mcodelog;
    }
    HTCodeLog createHTCodeLogByDto(HTAction mdb){
        var mcodelog=new HTCodeLog();
        mcodelog.setId(HTIDManager.getInstance().nextLong());
        mcodelog.setBId(mdb.getId());
        mcodelog.setCrt_DT(new Date());
        mcodelog.setFullName(mdb.getFullUrl() );
        mcodelog.setLX("Action");
        mcodelog.setMemo("");
        mcodelog.setName(mdb.getName());
        mcodelog.setPrj_Name(mdb.getPrj_Name());
        return mcodelog;
    }


    void refreshCtlToDB(){
        var ldb = htCtlMapper.selectList(new QueryWrapper<>()  );
        for(var mcls : lCtlAll)
        {
            dealCtrl(mcls, ldb );
        }
        for (var mdb : ldb)
        {
            var op=lCtlAll.stream().filter( a-> a.getFullName().equals(mdb.getCtlFullName()))
                    .findFirst()  ;

            if (!op.isPresent() && !mdb.isDeleted() )
            {
                mdb.setDeleted(true);
                htCtlMapper.updateById(mdb,true);
            }
        }
    }
    void dealCtrl(HTCtlDto mcls,List<HTCtl> ldb){
        var op=ldb.stream().filter(a->a.getCtlFullName().equals(mcls.getFullName()))
                .findFirst()
                 ;

        if(!op.isPresent()){
            var mdb=new HTCtl();
            mdb.setId(HTIDManager.getInstance().nextLong());
            mdb.setName(mcls.getName());
            mdb.setMemo(mcls.getMemo());
            mdb.setRoute(mcls.getRoute());
            mdb.setCtlFullName(mcls.getFullName());
            mdb.setPrj_Name(projectInfoProperty.getPrj_Name() );
            htCtlMapper.insert(mdb,true);

        }else{
            var mdb=op.get();
            if(!mdb.getName().equals(mcls.getName()) )
            {
                mdb.setName(mcls.getName() );
            }
            if(!mdb.getMemo().equals(mcls.getMemo()) )
            {
                mdb.setMemo(mcls.getMemo() );
            }
            if(!mdb.getRoute().equals(mcls.getRoute()) )
            {
                mdb.setRoute(mcls.getRoute() );
            }
            if(mdb.isDeleted() )
            {
                mdb.setDeleted(false);
            }
            htCtlMapper.updateById(mdb,true);
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public void refreshToDB(){
        refreshCtlToDB();
        refreshActionToDB();
    }
    void refreshActionToDB(){
        var lcls = htClsMapper.selectList(new QueryWrapper<>()  );
        var ldb = htActionMapper.selectList(new QueryWrapper<>()  );
        for(var mcls  :lActAll)
        {

            dealAction(mcls, ldb ,lcls );
        }
        for(var mdb:ldb){
            if(mdb.isDeleted()){
                continue;
            }
            var op=lActAll.stream().filter(a->a.getFullUrl().equals(mdb.getFullUrl()))
                    .findFirst();
            if(!op.isPresent()){
                mdb.setDeleted(true);
                htActionMapper.updateById(mdb,true);

                var mcodelog= createHTCodeLogByDto(mdb);
                mcodelog.setMemo("删除");
                htCodeLogMapper.insert(mcodelog,true);
            }
        }
    }
    void dealAction(HTCtlActDto mcls, List<HTAction> ldb,List<HTCls> lcls){
         var op=ldb.stream().filter(a->a.getFullUrl().equals(mcls.getFullUrl()))
                 .findFirst() ;
         if(!op.isPresent() ){
                var mdb=new HTAction();
                mdb.setId(HTIDManager.getInstance().nextLong());
                mdb.setPrj_Name(projectInfoProperty.getPrj_Name());
                mdb.setCtlFullName(mcls.getCtlFullName());
                mdb.setFullUrl(mcls.getFullUrl());
                mdb.setName(mcls.getName());
                mdb.setMemo(mcls.getMemo());
                mdb.setPinType(mcls.getPinType());
                mdb.setPinIsList(mcls.isPinIsList());
                mdb.setPoutType(mcls.getPoutType());
                mdb.setPoutIsList(mcls.isPoutIsList());
                mdb.setPoutIsPage(mcls.isPoutIsPage());
                mdb.setAuthType(mcls.getAuthType().toString() );
                mdb.setLogNeed(mcls.isLogNeed());
                mdb.setLogTemplete(mcls.getLogTemplete());
                mdb.setLogPin(mdb.isLogPin());
                mdb.setLogPout(mdb.isLogPout());
                htActionMapper.insert(mdb,true);

                 var mcodelog= createHTCodeLogByDto(mdb);
                 mcodelog.setMemo("新增");
                 htCodeLogMapper.insert(mcodelog,true);
         }
         else{
             var mdb=op.get();
             var msg=compareActionField(mcls,mdb);
             if (mdb.isDeleted())
             {
                 mdb.setDeleted(false);
                 msg = "删除恢复\r\n" + msg;
             }

             if (!"".equals(mdb.getPinType()))
             {
                 var op2=lcls.stream().filter(a -> a.getFullName().equals(mdb.getPinType()))
                         .findFirst();
                 if(op2.isPresent()){
                     var mcls2=op2.get();
                     if (  mcls2.getEdt_DT().getTime()> mdb.getEdt_DT().getTime() )
                     {

                         msg +=String.format("参数类型 %s 更新",mcls2.getFullName());
                     }
                 }

             }
             if (!"".equals(mdb.getPoutType()))
             {
                 var op2=lcls.stream().filter(a -> a.getFullName().equals(mdb.getPoutType()))
                         .findFirst();
                 if(op2.isPresent()){
                     var mcls2=op2.get();
                     if (   mcls2.getEdt_DT().getTime()> mdb.getEdt_DT().getTime() )
                     {
                         msg +=String.format("返回结果类型 %s 更新",mcls2.getFullName());
                     }
                 }

             }
             if (!"".equals(msg)){
                 htActionMapper.updateById(mdb,true);
                 var mcodelog= createHTCodeLogByDto(mdb);
                 mcodelog.setMemo(msg);
                 htCodeLogMapper.insert(mcodelog,true);

             }

         }
    }
    String compareAction(HTCtlActDto mcls, HTAction mdb,String fieldName){
        var v=HTClsManager.getInstance().getFieldStrV(mcls,fieldName);
        var dbv=HTClsManager.getInstance().getFieldStrV(mdb,fieldName);
        if(!v.equals(dbv)){
            var v2=HTClsManager.getInstance().getFieldV(mcls,fieldName);
            if(v2 instanceof Enum<?>){
                v2=v2.toString();
            }
            HTClsManager.getInstance().setFieldV(mdb,fieldName,v2);
            return fieldName+" "+dbv+"=>"+v+"\r\n";
        }
        return "";
    }

    String compareActionField(HTCtlActDto mcls, HTAction mdb )
    {
        var sb = new StringBuilder();
        sb.append(  compareAction(mcls,mdb,"CtlFullName") );
        sb.append(  compareAction(mcls,mdb,"FullUrl") );
        sb.append(  compareAction(mcls,mdb,"Name") );
        sb.append(  compareAction(mcls,mdb,"Memo") );
        sb.append(  compareAction(mcls,mdb,"PinType") );
        sb.append(  compareAction(mcls,mdb,"PinIsList") );
        sb.append(  compareAction(mcls,mdb,"PoutType") );
        sb.append(  compareAction(mcls,mdb,"PoutIsList") );
        sb.append(  compareAction(mcls,mdb,"PoutIsPage") );
        sb.append(  compareAction(mcls,mdb,"AuthType") );
        sb.append(  compareAction(mcls,mdb,"LogNeed") );
        sb.append(  compareAction(mcls,mdb,"LogTemplete") );
        sb.append(  compareAction(mcls,mdb,"LogPin") );
        sb.append(  compareAction(mcls,mdb,"LogPout") );
        return  sb.toString();
    }


    public HTCtlDto getCtl(String fullname)
    {
        String finalUrl = fullname;
        var m = StreamUtil.get(lCtlAll,a->a.getFullName().equals(finalUrl));
        return m;
    }

    public HTCtlActDto getAction(String url)
    {
        url = url.toLowerCase();
        String finalUrl = url;
        var m = StreamUtil.get(lActAll,a->a.getFullUrl().equals(finalUrl));
        return m;
    }

}
